package library

import (
	"context"
	"sync"
)

type executeHandler struct {
	handler func(c context.Context, param map[string]interface{}) (interface{}, error)
	isBreak bool
	key     string
}

type Executer struct {
	parallel []executeHandler
	serial   []executeHandler
	params   map[string]interface{}
	ctx      context.Context
}

func NewExecuter() *Executer {
	return &Executer{parallel: make([]executeHandler, 0), serial: make([]executeHandler, 0), params: map[string]interface{}{}}
}
func (this *Executer) WithParam(key string, param interface{}) *Executer {
	this.params[key] = param
	return this
}

// 并行
func (this *Executer) WithParaller(ctx context.Context, handler func(c context.Context, param map[string]interface{}) (interface{}, error), isBreak bool, key string) *Executer {
	var exHandler = executeHandler{handler: handler, isBreak: isBreak, key: key}
	this.parallel = append(this.parallel, exHandler)
	this.ctx = ctx
	return this
}

// 串行
func (this *Executer) WithSerial(ctx context.Context, handler func(c context.Context, param map[string]interface{}) (interface{}, error), isBreak bool, key string) *Executer {
	var exHandler = executeHandler{handler: handler, isBreak: isBreak, key: key}
	this.serial = append(this.serial, exHandler)
	this.ctx = ctx
	return this
}
func (this *Executer) parallelExecute() {
	var waitGroup = sync.WaitGroup{}
	waitGroup.Add(len(this.parallel))
	for _, handler := range this.parallel {
		go func() {
			defer waitGroup.Done()
			handler.handler(this.ctx, this.params)
		}()
	}
	waitGroup.Wait()
}
func (this *Executer) Run() (map[string]interface{}, error) {
	var err error
	for _, handler := range this.serial {
		var rs interface{}
		rs, err = handler.handler(this.ctx, this.params)
		if err != nil && handler.isBreak {
			return this.params, err
		} else {
			this.WithParam(handler.key, rs)
		}
	}
	go this.parallelExecute()
	return this.params, err
}
